// ----------------------------------------------------------------------------
// serialize_schema.cpp
// ----------------------------------------------------------------------------
#include <fw/base/serialize/serialize_schema.h>
#include <fw/base/serialize/serialize_compound_type.h>
#include <fw/base/serialize/serialize_float_scalar_type.h>
#include <fw/base/serialize/serialize_int_scalar_type.h>
#include <fw/base/serialize/byte_reader.h>
#include <fw/base/serialize/byte_writer.h>
#include <fw/base/core/assertion.h>

namespace fw
{
	SerializeSchema::SerializeSchema()
	: m_state			(kState_New)
	, m_currentCompound	(nullptr)
	, m_dictionary		(16, nullptr)
	{
	}
	
	SerializeSchema::~SerializeSchema()
	{
		for(int dic_i=0; dic_i<m_dictionary.GetCount(); ++dic_i)
		{
			delete m_dictionary.GetByIndex(dic_i);
		}
	}
	
	const SerializeType*	SerializeSchema::FindType(Hash name) const
	{
		return m_dictionary.FindByName(name);
	}
	
	SerializeType*			SerializeSchema::FindType(Hash name)
	{
		return m_dictionary.FindByName(name);
	}

	void					SerializeSchema::Begin()
	{
		FW_ASSERT(m_state == kState_New);
		m_state = kState_Building;
		
		// 組み込み型の定義を行っておく. 
		AddType("uint8_t",		new SerializeIntScalarType< uint8_t>);
		AddType("int8_t",		new SerializeIntScalarType<  int8_t>);
		AddType("uint16_t",		new SerializeIntScalarType<uint16_t>);
		AddType("int16_t",		new SerializeIntScalarType< int16_t>);
		AddType("uint32_t",		new SerializeIntScalarType<uint32_t>);
		AddType("int32_t",		new SerializeIntScalarType< int32_t>);
		AddType("uint64_t",		new SerializeIntScalarType<uint64_t>);
		AddType("int64_t",		new SerializeIntScalarType< int64_t>);
		AddType("float",		new SerializeFloatScalarType< float>);
		AddType("double",		new SerializeFloatScalarType<double>);
	}
	
	void					SerializeSchema::End()
	{
		FW_ASSERT(m_state == kState_Building);
		FixUp();
		m_state = kState_Ready;
	}

	void					SerializeSchema::BeginCompound(Hash typeName)
	{
		FW_ASSERT(!m_currentCompound);
		
		m_currentName		= typeName;
		m_currentCompound	= new SerializeCompoundType();
	}
	
	void					SerializeSchema::EndCompound()
	{
		AddType(m_currentName, m_currentCompound);
		m_currentName		= Hash();
		m_currentCompound	= nullptr;
	}
	
	char* SerializeSchema::Convert(const char *srcData, const SerializeSchema &srcSchema, Hash name, SerializeAllocator &alloc) const
	{
		const SerializeType* srcType = srcSchema.FindType(name);
		const SerializeType* dstType =           FindType(name);
		
		char*  dstPtr = alloc.Alloc(dstType, srcType, srcData, 1);
		alloc.ConvertAll();
		
		return dstPtr;
	}
	
	void SerializeSchema::AddInstance(Hash memberName, int count, Hash typeName)
	{
		FW_ASSERT(m_currentCompound);
		m_currentCompound->AddInstance(memberName, count, typeName);
	}

	void SerializeSchema::AddPointer(Hash memberName, int count, Hash typeName)
	{
		FW_ASSERT(m_currentCompound);
		m_currentCompound->AddPointer(memberName, count, typeName);
	}
	
	void SerializeSchema::AddCounterPointer(Hash memberName, int count, Hash typeName, Hash countName)
	{
		FW_ASSERT(m_currentCompound);
		m_currentCompound->AddCountedPointer(memberName, count, typeName, countName);
	}
	
	void SerializeSchema::AddStringPointer(Hash								memberName,
										   int								count,
										   Hash								typeName,
										   Hash								terminatorName,
										   const fw::SerializeScalarValue&	terminatorValue)
	{
		FW_ASSERT(m_currentCompound);
		m_currentCompound->AddStringPointer(memberName, count, typeName, terminatorName, terminatorValue);
	}
	
	SerializeStatus			SerializeSchema::FixUp(Hash typeName)
	{
		SerializeStatus status  = kStatus_Ok;
		SerializeType*	typeDef = m_dictionary.FindByName(typeName);
		if(!typeName)
		{
			status = SerializeStatus::kStatus_ErrorNodeNotFound;
		}
		else
		{
			typeDef->FixUp(*this);
		}
		return status;
	}
	
	void					SerializeSchema::Write(ByteWriter& writer) const
	{
		int count           = m_dictionary.GetCount();
		int serializedCount = count;
		
		for(int dic_i=0; dic_i<count; ++dic_i)
		{
			const SerializeType* t = m_dictionary.GetByIndex(dic_i);
			if(t->IsBuiltIn()) // uint8_tのような組み込み型の場合はスキップする.
			{
				serializedCount--;
			}
		}
		
		writer.Write16(serializedCount);
		
		for(int dic_i=0; dic_i<count; ++dic_i)
		{
			const SerializeType* t = m_dictionary.GetByIndex(dic_i);
			if(!t->IsBuiltIn())
			{
				Hash name = m_dictionary.GetNameByIndex(dic_i);
				writer.Write32(name);
				writer.Write(t);
			}
		}
	}
	
	SerializeSchema*		SerializeSchema::ReadNew(ByteReader &reader)
	{
		size_t           rollBack = reader.Tell();
		SerializeSchema* schema   = new SerializeSchema();
		FW_ASSERT(schema);
		
		int count = reader.Read16();
		schema->Begin();
		
		for(int tp_i=0; tp_i<count; ++tp_i)
		{
			Hash           typeName      = reader.Read32();
			SerializeType* serializeType = reader.ReadNew<SerializeType>();
			
			if(serializeType)
			{
				schema->AddType(typeName, serializeType);
			}
			else
			{
				delete schema;
				schema = nullptr;
				break;
			}
		}
		
		if(!schema)
		{
			reader.Seek(rollBack);
		}
		else
		{
			schema->End();
		}
		return schema;
	}
	
	uint64_t				SerializeSchema::GetChecksum(uint64_t basis) const
	{
		int count = m_dictionary.GetCount();
		
		for(int dic_i=0; dic_i<count; ++dic_i)
		{
			const SerializeType* t = m_dictionary.GetByIndex(dic_i);
			if(t->IsBuiltIn())
			{
				Hash name = m_dictionary.GetNameByIndex(dic_i);
				basis = HashValue64(static_cast<uint32_t>(name), basis);
				basis = t->GetChecksum(basis);
			}
		}
		return basis;
	}
	
	SerializeStatus			SerializeSchema::FixUp()
	{
		SerializeStatus status = kStatus_Ok;
		for(int dic_i=0; dic_i<m_dictionary.GetCount(); ++dic_i)
		{
			status = m_dictionary.GetByIndex(dic_i)->FixUp(*this);
			if(status!=kStatus_Ok)
			{
				break;
			}
		}
		return status;
	}
	
	void					SerializeSchema::AddType(Hash typeName, SerializeType *serializeType)
	{
		m_dictionary.Add(typeName, serializeType);
	}
	
} // namespace fw
